﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using Microsoft.Win32;

namespace CHomeWeather
{
    public class UpdateService
    {
        private const char TARGET_SEPARATOR = ';';
        private const string ARGUMENT_CITY = "#city#";
        private const string ARGUMENT_ICON_PATH = "#iconfolder#";

        private string _currentCity;
        private string _iconFolder;
        private Template _template;
        private Configuration _configuration;

        /// <summary>
        /// Initializes a new instance of <see cref="UpdateService"/>.
        /// </summary>
        public UpdateService(Template template, Configuration configuration)
        {
            _template = template;
            _configuration = configuration;
        }

        public void Update()
        {
            _currentCity = _configuration.Cities[0];

            if (!Util.IsAbsolutePath(_configuration.IconFolder))
            {
                _iconFolder = Path.Combine(Util.ApplicationFolder, _configuration.IconFolder);

                if (_iconFolder.EndsWith("\\"))
                {
                    _iconFolder = _iconFolder.Substring(0, _iconFolder.Length - 1);
                }
            }

            List<WeatherInfo> weatherInfos = new List<WeatherInfo>();

            foreach (var query in _template.Queries)
            {
                string content = GetWebContent(ResolveArgument(query.Url));

                foreach (var update in query.UpdateConfigurations)
                {
                    string mergedTarget = update.Targets;

                    if (string.IsNullOrEmpty(mergedTarget))
                    {
                        continue;
                    }

                    string[] targets = mergedTarget.Split(TARGET_SEPARATOR);
                    List<WeatherInfo> updates = GetUpdateds(content, update);

                    for (int i = 0; i < targets.Length; i++)
                    {
                        updates[i].Target = targets[i];

                        var item = weatherInfos.SingleOrDefault(x => x.Target == targets[i]);

                        if (null != item)
                        {
                            // TODO:
                        }
                        else
                        {
                            weatherInfos.Add(updates[i]);
                        }
                    }
                }
            }

            if (null == weatherInfos || weatherInfos.Count == 0)
            {
                return;
            }

            foreach (var item in weatherInfos)
            {
                RegistryKey key = Registry.LocalMachine.OpenSubKey(_configuration.TargetRegistryKey + item.Target, true);

                foreach (var keyValue in item)
                {
                    key.SetValue(keyValue.Key, keyValue.Value);
                }
            }
        }

        private List<WeatherInfo> GetUpdateds(string content, UpdateConfiguration update)
        {
            List<WeatherInfo> weatherInfos = new List<WeatherInfo>();

            foreach (var matchconfig in update.Matches)
            {
                Regex regex = new Regex(matchconfig.Pattern);
                MatchCollection matches = regex.Matches(content);
                string[] groupNames = regex.GetGroupNames();

                for (int i = 0; i < matches.Count; i++)
                {
                    Match match = matches[i];

                    if (weatherInfos.Count == i)
                    {
                        weatherInfos.Add(new WeatherInfo());
                    }

                    WeatherInfo info = weatherInfos[i];

                    foreach (var groupName in groupNames)
                    {
                        if (Regex.IsMatch(groupName, "^\\d+$"))
                        {
                            continue;
                        }

                        Group group = match.Groups[groupName];

                        if (group.Success)
                        {
                            var result = group.Value;

                            if (null != matchconfig.Replacements)
                            {
                                foreach (var replacement in matchconfig.Replacements)
                                {
                                    result = Regex.Replace(result, replacement.From, ResolveArgument(replacement.To));
                                }
                            }

                            if (info.ContainsKey(groupName))
                            {
                                info[groupName] = result;
                            }
                            else
                            {
                                info.Add(groupName, result);
                            }
                        }
                    }
                }
            }

            return weatherInfos;
        }

        private string ResolveArgument(string valueWithArgument)
        {
            string result = valueWithArgument;

            if (valueWithArgument.IndexOf(ARGUMENT_CITY, StringComparison.InvariantCultureIgnoreCase) >= 0)
            {
                result = Regex.Replace(valueWithArgument, ARGUMENT_CITY, _currentCity, RegexOptions.IgnoreCase);
            }

            if (valueWithArgument.IndexOf(ARGUMENT_ICON_PATH, StringComparison.InvariantCultureIgnoreCase) >= 0)
            {
                result = Regex.Replace(valueWithArgument, ARGUMENT_ICON_PATH, _iconFolder, RegexOptions.IgnoreCase);
            }

            return result;
        }

        /// <summary>
        /// Downloads web content from a url.
        /// </summary>
        private string GetWebContent(string url)
        {
            string content;
            WebRequest request = HttpWebRequest.Create(url);
            WebResponse response = request.GetResponse();

            using(StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                content = reader.ReadToEnd();
            }

            return content;
        }
    }
}
